﻿using iDetector;
using System;
using System.Diagnostics;

namespace DART.Detector.IRay.Wrapper
{
    /// <summary>
    /// IRay曝光参数
    /// </summary>
    public class IRayModeParam
    {
        /// <summary>
        /// 应用模式，默认为<see cref="AppMode.Contact"/>。
        /// </summary>
        public AppMode AppMode { get; set; }

        /// <summary>
        /// 曝光模式，默认<see cref="ExpModeEx.Null"/> (校准模式)。
        /// </summary>
        public ExpModeEx ExpMode { get; set; }

        /// <summary>
        /// 滤过，默认为<see cref="Filter.Filter1"/>。
        /// </summary>
        public Filter Filter { get; set; }

        /// <summary>
        /// 校准模式，默认为<see cref="CorrectOption.None"/>。
        /// </summary>
        public CorrectOption CorrectOption { get; set; }

        /// <summary>
        /// 触发模式，默认<see cref="TriggerMode.Prep"/>。仅2D采集支持 <see cref="TriggerMode.Outer"/>
        /// </summary>
        public TriggerMode TriggerMode { get; set; } = TriggerMode.Prep;

        /// <summary>
        /// AEC已启用。
        /// </summary>
        public bool AutoExposure => ExpMode == ExpModeEx.AEC || ExpMode == ExpModeEx.TomoAEC;

        /// <summary>
        /// 快速设置，Stereo模式下AEC不可用,会切换到Contact AEC。
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="autoExposure"></param>
        /// <param name="filter"></param>
        public static IRayModeParam Create(AppMode mode, bool autoExposure, Filter filter = Filter.Filter1)
        {
            var o = new IRayModeParam
            {
                AppMode = mode,
                Filter = filter,
            };
            switch (mode)
            {
                case AppMode.Contact:
                case AppMode.Magnification:
                case AppMode.Stereo:
                    o.AppMode = autoExposure && mode == AppMode.Stereo ? AppMode.Contact : mode; // Stereo模式下AEC不可用,切换到Contact AEC
                    o.ExpMode = o.AppMode == AppMode.Stereo ? ExpModeEx.StereoManual : (autoExposure ? ExpModeEx.AEC : ExpModeEx.Manual);
                    o.CorrectOption = CorrectOption.HWPostOffset | CorrectOption.SWGain | CorrectOption.SWDefect;
                    o.TriggerMode = TriggerMode.Outer;
                    break;

                case AppMode.Tomo:
                    o.ExpMode = autoExposure ? ExpModeEx.TomoAEC : ExpModeEx.TomoManual;
                    o.CorrectOption = CorrectOption.HWPreOffset | CorrectOption.SWGain | CorrectOption.SWDefect;
                    o.TriggerMode = TriggerMode.Prep;
                    break;

                default:
                    break;
            }
            return o;
        }

        /// <summary>
        /// 快速设置，Stereo模式下AEC不可用,会切换到Contact AEC。
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="autoExposure"></param>
        /// <param name="triggerMode"></param>
        /// <param name="filter"></param>
        public static IRayModeParam Create(AppMode mode, bool autoExposure, TriggerMode triggerMode, Filter filter = Filter.Filter1)
        {
            var o = new IRayModeParam
            {
                AppMode = mode,
                TriggerMode = triggerMode,
                Filter = filter,
            };
            switch (mode)
            {
                case AppMode.Contact:
                case AppMode.Magnification:
                case AppMode.Stereo:
                    o.AppMode = autoExposure && mode == AppMode.Stereo ? AppMode.Contact : mode; // Stereo模式下AEC不可用,切换到Contact AEC
                    o.ExpMode = o.AppMode == AppMode.Stereo ? ExpModeEx.StereoManual : (autoExposure ? ExpModeEx.AEC : ExpModeEx.Manual);
                    o.CorrectOption = CorrectOption.HWPostOffset | CorrectOption.SWGain | CorrectOption.SWDefect;
                    break;

                case AppMode.Tomo:
                    o.ExpMode = autoExposure ? ExpModeEx.TomoAEC : ExpModeEx.TomoManual;
                    o.CorrectOption = CorrectOption.HWPreOffset | CorrectOption.SWGain | CorrectOption.SWDefect;
                    break;

                default:
                    break;
            }
            return o;
        }
    }

    /// <summary>
    /// 2D主曝光附加参数
    /// </summary>
    public class IRayDetectorParam
    {
        /// <summary>
        /// 采集时间窗口(ms)
        /// </summary>
        public int ExpWin { get; set; }

        /// <summary>
        /// 采集模式，用于设置多帧叠加采集
        /// </summary>
        public PrepCapMode AcqType { get; set; }

        /// <summary>
        /// 常量 3D为3,  2D为6
        /// </summary>
        public int PGA { get; set; }
    }

    /// <summary>
    /// IRay校准模式参数
    /// </summary>
    public class IRayCalibrationParam
    {
        /// <summary>
        /// 电压(kV)
        /// </summary>
        public int Kilovolt { get; set; }

        /// <summary>
        /// 期望灰度 [0,65535]
        /// </summary>
        public int Grayscale { get; set; }
    }

    /// <summary>
    /// IRay命令参数
    /// </summary>
    public class IRayCmdParam
    {
        private iDetector.IRayCmdParam param = new iDetector.IRayCmdParam();

        public IRayCmdParam(object value)
        {
            if (value is IRayCmdParam p)
            {
                param = p.param;
            }
            else if (value is iDetector.IRayCmdParam v)
            {
                param = v;
            }
            else
            {
                param.pt = IRAY_PARAM_TYPE.IPT_VARIANT;
                if (value is sbyte || value is short || value is int || value is long
                    || value is byte || value is ushort || value is uint || value is ulong || value is Enum)
                {
                    param.var.vt = IRAY_VAR_TYPE.IVT_INT;
                    param.var.val.nVal = (int)value;
                }
                else if (value is float || value is double || value is decimal)
                {
                    param.var.vt = IRAY_VAR_TYPE.IVT_FLT;
                    param.var.val.fVal = (float)value;
                }
                else
                {
                    param.var.vt = IRAY_VAR_TYPE.IVT_STR;
                    param.var.val.strVal = $"{value}";
                }
            }
        }

        public static implicit operator IRayCmdParam(string value) => new IRayCmdParam(value);

        public static implicit operator IRayCmdParam(int value) => new IRayCmdParam(value);

        public static implicit operator IRayCmdParam(double value) => new IRayCmdParam(value);

        public static implicit operator IRayCmdParam(Enum value) => new IRayCmdParam(value);

        public static implicit operator iDetector.IRayCmdParam(IRayCmdParam value) => value.param;
    }

    /// <summary>
    /// IRay属性值
    /// </summary>
    [DebuggerDisplay("{ToString()}")]
    public class IRayVariant
    {
        private iDetector.IRayVariant param = new iDetector.IRayVariant();

        public IRayVariant(object value)
        {
            if (value is IRayVariant v)
            {
                param = v.param;
            }
            else if (value is iDetector.IRayVariant vv)
            {
                param = vv;
            }
            else if (value is sbyte || value is short || value is int || value is long
                || value is byte || value is ushort || value is uint || value is ulong || value is Enum)
            {
                param.vt = IRAY_VAR_TYPE.IVT_INT;
                param.val.nVal = (int)value;
            }
            else if (value is float || value is double || value is decimal)
            {
                param.vt = IRAY_VAR_TYPE.IVT_FLT;
                param.val.fVal = (float)value;
            }
            else
            {
                param.vt = IRAY_VAR_TYPE.IVT_STR;
                param.val.strVal = $"{value}";
            }
        }

        public T To<T>()
        {
            object value = null;
            switch (param.vt)
            {
                case IRAY_VAR_TYPE.IVT_INT:
                    value = param.val.nVal;
                    break;

                case IRAY_VAR_TYPE.IVT_FLT:
                    value = param.val.fVal;

                    break;

                case IRAY_VAR_TYPE.IVT_STR:
                    value = param.val.strVal;
                    break;

                default:
                    break;
            }
            return value is T v ? v : (T)Convert.ChangeType(value, typeof(T));
        }

        public override string ToString()
        {
            return $"{To<object>()}";
        }

        public static implicit operator IRayVariant(string variant) => new IRayVariant(variant);

        public static implicit operator IRayVariant(int variant) => new IRayVariant(variant);

        public static implicit operator IRayVariant(float variant) => new IRayVariant(variant);

        public static implicit operator IRayVariant(iDetector.IRayVariant variant) => new IRayVariant(variant);

        public static implicit operator iDetector.IRayVariant(IRayVariant variant) => variant.param;
    }

    /// <summary>
    /// IRay属性信息
    /// </summary>
    public class IRayAttrInfo
    {
        public int AttrID { get; private set; }

        /// <summary>
        /// IRAY_VAR_TYPE:
        /// IVT_INT = 0,
        /// IVT_FLT = 1,
        /// IVT_STR = 2,
        /// </summary>
        public int DataType { get; private set; }

        public int IsConfigItem { get; private set; }
        public int IsWritable { get; private set; }
        public int IsEnum { get; private set; }
        public int Precision { get; private set; }
        public float MinValue { get; private set; }
        public float MaxValue { get; private set; }
        public string Path { get; private set; }
        public string Name { get; private set; }
        public string DisplayName { get; private set; }
        public string Unit { get; private set; }
        public string Description { get; private set; }
        public string EnumTypeName { get; private set; }

        /// <summary>
        /// PARAM_VALIDATOR:
        /// Validator_Null = 0,
        /// Validator_MinMax = 1,
        /// Validator_Enum = 2,
        /// Validator_FilePath = 3,
        /// Validator_IP = 4,
        /// Validator_MAC = 5,
        /// Validator_FpdSN = 6,
        /// </summary>
        public int Validator { get; set; }

        public Type GetDataType()
        {
            if (DataType == 0) return typeof(int);
            else if (DataType == 1) return typeof(float);
            else return typeof(string);
        }

        public static implicit operator AttrInfo(IRayAttrInfo attrInfoClass)
        {
            return new AttrInfo
            {
                nAttrID = attrInfoClass.AttrID,
                nDataType = (IRAY_VAR_TYPE)attrInfoClass.DataType,
                bIsConfigItem = attrInfoClass.IsConfigItem,
                bIsWritable = attrInfoClass.IsWritable,
                bIsEnum = attrInfoClass.IsEnum,
                nPrecision = attrInfoClass.Precision,
                fMinValue = attrInfoClass.MinValue,
                fMaxValue = attrInfoClass.MaxValue,
                strPath = attrInfoClass.Path,
                strName = attrInfoClass.Name,
                strDisplayName = attrInfoClass.DisplayName,
                strUnit = attrInfoClass.Unit,
                strDescription = attrInfoClass.Description,
                strEnumTypeName = attrInfoClass.EnumTypeName,
                eValidator = (PARAM_VALIDATOR)attrInfoClass.Validator
            };
        }

        public static implicit operator IRayAttrInfo(AttrInfo attrInfo)
        {
            return new IRayAttrInfo
            {
                AttrID = attrInfo.nAttrID,
                DataType = (int)attrInfo.nDataType,
                IsConfigItem = attrInfo.bIsConfigItem,
                IsWritable = attrInfo.bIsWritable,
                IsEnum = attrInfo.bIsEnum,
                Precision = attrInfo.nPrecision,
                MinValue = attrInfo.fMinValue,
                MaxValue = attrInfo.fMaxValue,
                Path = attrInfo.strPath,
                Name = attrInfo.strName,
                DisplayName = attrInfo.strDisplayName,
                Unit = attrInfo.strUnit,
                Description = attrInfo.strDescription,
                EnumTypeName = attrInfo.strEnumTypeName,
                Validator = (int)attrInfo.eValidator
            };
        }
    }
}